########################################################################
#  Wiizard - A Wii games manager
#  Copyright (C) 2023  CYBERDEViL
#
#  This file is part of Wiizard.
#
#  Wiizard is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  Wiizard is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
########################################################################


import os

from PyQt5.QtCore import pyqtSignal

import pywwt

from wiizard.thread import AbstractThread, THREAD_FLAG_IS_CANCELLABLE
from wiizard.models.source import SOURCE_TYPE_LOCAL


GAME_OP_DELETE = 1
GAME_OP_ADD    = 2
GAME_OP_MOVE   = GAME_OP_ADD | GAME_OP_DELETE


class GameOperationsThread(AbstractThread):
  progress = pyqtSignal(int, str)  # operation index, error message

  def __init__(self, operations):
    AbstractThread.__init__(self, flags=THREAD_FLAG_IS_CANCELLABLE)
    self.__operations = operations

  def execAdd(self, op):
    error     = ""
    fromModel = op.game.sourceModel
    toModel   = op.target

    if op.game.fileSize > toModel.getFree():
      error = "Not enough free space!"
      return error

    if fromModel.type == SOURCE_TYPE_LOCAL:
      # Local to local (regular file copy)
      if toModel.type == SOURCE_TYPE_LOCAL:
        frompath = os.path.join(op.game.sourcePath, op.game.filename)
        topath   = os.path.join(toModel.location, op.game.filename)
        opStr    = f"copy {frompath} to {topath}."

        print(f"Try to {opStr}")
        error  = f"Failed to {opStr}"
        error += "\nFIXME copy local to local not implemented yet."

      # Local to WBFS partition
      else:
        filepath = os.path.join(op.game.sourcePath, op.game.filename)
        opStr    = f"add '{filepath}' to '{toModel.location}'."
        result   = None

        print(f"Try to {opStr}")

        try:
          result = pywwt.add_to_wbfs(toModel.location, filepath, testMode=False)
        except pywwt.error as err:
          error  = f"Failed to {opStr}"
          error += f"\npywwt.error: '{err}'"
        else:
          if not result:
            error = f"Failed to {opStr}"

    else:
      # WBFS partition to local
      if toModel.type == SOURCE_TYPE_LOCAL:
        # TODO check file type
        # TODO check if file exists
        frompath = f"{fromModel.location}:{op.game.id6}"
        topath   = os.path.join(toModel.location, f"{op.game.id6}.wbfs")
        opStr    = f"export {frompath} to {topath}."
        result   = None

        print(f"Try to {opStr}")

        try:
          result = pywwt.extract_from_wbfs(op.game.sourceModel.location,
                                           op.game.id6,
                                           topath,
                                           testMode=False)
        except pywwt.error as err:
          error  = f"Failed to {opStr}"
          error += f"\npywwt.error: '{err}'"

        else:
          if not result:
            error  = f"Failed to {opStr}"

      # WBFS partition to WBFS partition
      else:
        frompath = f"{fromModel.location}:{op.game.id6}"
        topath   = f"{toModel.location}:{op.game.id6}"
        opStr    = f"copy {frompath} to {topath}."
        result   = None

        print(f"Try to {opStr}")

        try:
          result = pywwt.wbfs_to_wbfs(fromModel.location, toModel.location,
                                      op.game.id6)
        except pywwt.error as err:
          error  = f"Failed to {opStr}"
          error += f"\npywwt.error: '{err}'"

    return error

  def execDelete(self, op):
    fromModel = op.game.sourceModel
    error     = ""
    if fromModel.type == SOURCE_TYPE_LOCAL:
      # Delete local file
      filepath = os.path.join(op.game.sourcePath, op.game.filename)
      opStr    = f"delete {filepath}."

      print(f"Try to {opStr}")
      error  = f"Failed to {opStr}"
      error += "\nFIXME Delete local file not implemented yet."
    else:
      # Delete game from WBFS partition
      filepath = f"{fromModel.location}:{op.game.id6}"
      opStr    = f"delete {filepath}."
      result   = None

      print(f"Try to {opStr}")

      try:
        result = pywwt.remove_from_wbfs(fromModel.location, op.game.id6,
                                        testMode=False)
      except pywwt.error as err:
        error  = f"Failed to {opStr}"
        error += f"\npywwt.error: '{err}'"
      else:
        if not result:
          error  = f"Failed to {opStr}"

    return error

  def run(self):
    progress = 0

    for index in range(0, len(self.__operations)):
      error = ""
      op    = self.__operations[index]

      # ADD game
      if op.operation == GAME_OP_ADD:
        error = self.execAdd(op)

      # DELETE game
      elif op.operation == GAME_OP_DELETE:
        error = self.execDelete(op)

      # MOVE game (First ADD and then DELETE)
      elif op.operation == GAME_OP_MOVE:
        error = self.execAdd(op)
        if not error:
          error = self.execDelete(op)

      progress += 1
      self.progress.emit(progress, error)

      if self.cancelled is True:
          break

    self.completed.emit()


class GameOperation:
  def __init__(self, game, targetModel, operation):
    self.__game        = game
    self.__targetModel = targetModel
    self.__operation   = operation

  @property
  def operation(self):
    return self.__operation

  @property
  def game(self):
    return self.__game

  @property
  def target(self):
    return self.__targetModel
